package com.appleyk.sso.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * <p>授权服务配置</p>
 *
 * @author appleyk
 * @version V.0.1.1
 * @blob https://blog.csdn.net/appleyk
 * @github https://github.com/kobeyk
 * @date created on 8:46 2021/1/27
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizeServerConfig extends AuthorizationServerConfigurerAdapter {

    // 令牌存储策略
    @Autowired
    private TokenStore tokenStore;

    // 客户端详情服务
    @Autowired
    private ClientDetailsService clientDetailsService;

    // 认证管理器 （用户、密码、资源授权）
    @Autowired
    private AuthenticationManager authenticationManager;

    // 授权码服务
    @Autowired
    private AuthorizationCodeServices codeServices;

    // 1. 客户端详情服务配置(第一项配置)
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("c1") // client_id：客户端ID（如果多个客户端需要配置多个，正常情况下是放在数据库中进行存储的）
                .secret(new BCryptPasswordEncoder().encode("secret")) // client_secret：客户端秘钥
                .resourceIds("r1", "r2") // 资源列表
                /**
                 * 客户端授权方式，共四种：
                 * 1. authorization_code:授权码模式
                 * 2. password：密码模式
                 * 3.client_credentials:客户端模式
                 * 4.implicit：隐式模式（最简化的）
                 */
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                .scopes("app") // 允许授权的域（范围）,资源服务器这个scope必须和这个id的scope保持一致
                .autoApprove(false) // false则跳转到授权页面让用户授权，true则不跳转
                // 授权成功后，授权服务器重定向的客户端地址（回调地址）
                .redirectUris("http://www.baidu.com");
    }

    // 授权认证服务器的令牌服务配置
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        // 设置令牌存储策略
        services.setTokenStore(tokenStore);
        // 设置客户端详情服务
        services.setClientDetailsService(clientDetailsService);
        // 设置支持令牌刷新
        services.setSupportRefreshToken(true);
        // 设置令牌的访问有效期为3600秒，即1小时
        services.setAccessTokenValiditySeconds(3600);
        // 设置令牌的刷新时间为259200秒，即3天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

    // 授权码模式的授权码服务配置
    @Bean
    public AuthorizationCodeServices codeServices() {
        // 为了方便快速测试，暂时使用内存的存取方式（即授权code是基于内存进行读取的）
        return new InMemoryAuthorizationCodeServices();
    }

    // 2.配置授权认证服务器的令牌访问端点（第二项配置）
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager) // 设置认证管理器（来自security的配置）
                .authorizationCodeServices(codeServices) // 设置授权码模式下的授权码服务
                .tokenServices(tokenServices()) // 设置令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST); // 设置令牌访问端点的http请求方式为：post
    }

    // 3.授权认证服务器安全配置（第三项配置），主要配置token url的访问约束
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()") // ../oauth/token_key是公开的（不用登陆就可以获取公开的秘钥，这是对资源服务公开的）
                .checkTokenAccess("permitAll()") // ../oauth/check_token是公开的(也是对资源服务而言的)
                .allowFormAuthenticationForClients(); // 允许表单认证（通过表单来申请令牌）
    }

}
